-
Gdext .
-
Rust binding for Godot 4.
-
-
GitHub examples folder.
-
This is the most important part.
-
-
Documentation
Challenges
-
-
I agree about the frictions.
-
-
Access to classes external to those created via Rust .
-
Example: classes created via GDScript, GDExtension, etc.
-
Setup
-
cargo add godot-
I think this is only necessary if you don't manually define Godot in the Dependencies tab of Cargo.toml.
-
-
cargo new NAME --lib -
It must be compiled as a 'dynamic C library':
[lib] crate-type = ["cdylib"]
Attributes
Document in Godot
-
You can document your functions, classes, members, and signals with the
///doc comment syntax.
Derive Macros
derive(GodotClass)
-
You should use this macro; manual implementations of the
GodotClasstrait are not encouraged. -
This is typically used in combination with
#[godot_api], which can implement custom functions and constants, as well as override virtual methods.
#[derive(GodotClass)]
{
// Attributes available to this derive:
#[class]
#[base]
#[hint]
#[var]
#[export]
#[init]
}
derive(GodotConverter)
-
-
Indicates that a type can be passed to/from Godot, either directly or through an intermediate “via” type.
-
The associated type
Viaspecifies how this type is passed across the FFI boundary to/from Godot. GenerallyToGodotneeds to be implemented to pass a type to Godot, andFromGodotto receive this type from Godot. -
GodotTypeis a stronger bound thanGodotConvert, since it expresses that a type is directly representable in Godot (without intermediate “via”). EveryGodotTypealso implementsGodotConvertwithVia = Self. -
Please read the
godot::metamodule docs for further information about conversions.
-
#[derive(GodotConvert)]
{
// Attributes available to this derive:
#[godot]
}
derive(Var)
-
Derive macro for
Varon enums. -
This expects a derived
GodotConvertimplementation, using a manual implementation ofGodotConvertmay lead to incorrect values being displayed in Godot.
#[derive(Var)]
{
// Attributes available to this derive:
#[godot]
}
derive(Export)
#[derive(Export)]
{
// Attributes available to this derive:
#[godot]
}
Attribute Macros
var
#[derive(GodotClass)]
struct MyStruct {
#[var]
my_field: i64,
}
-
This makes the field accessible in GDScript using
my_struct.my_fieldsyntax. Additionally, it generates a trivial getter and setter namedget_my_fieldandset_my_field, respectively. These arepubin Rust, since they’re exposed from GDScript anyway. -
If you want to implement your own getter and/or setter, write those as a function on your Rust type, expose it using
#[func], and annotate the field with#[var(get = ..., set = ...)]:
#[derive(GodotClass)]
#[class(init)]
struct MyStruct {
#[var(get = get_my_field, set = set_my_field)]
my_field: i64,
}
#[godot_api]
impl MyStruct {
#[func]
pub fn get_my_field(&self) -> i64 {
self.my_field
}
#[func]
pub fn set_my_field(&mut self, value: i64) {
self.my_field = value;
}
}
struct MyStruct {
// Default getter, custom setter.
#[var(get, set = set_my_field)]
my_field: i64,
}
export
-
For exporting properties to the editor, you can use the
#[export]attribute:
#[derive(GodotClass)]
struct MyStruct {
#[export]
my_field: i64,
}
-
There is no need to use
#[var]together with export.
const MAX_HEALTH: f64 = 100.0;
#[derive(GodotClass)]
struct MyStruct {
#[export]
float: f64,
#[export(range = (0.0, 10.0, or_greater))]
range_f64: f64,
#[export(file)]
file: GString,
#[export(file = "*.gd")]
gdscript_file: GString,
#[export(flags_3d_physics)]
physics: u32,
#[export(exp_easing)]
ease: f64,
// @export_enum("One", "Two", "Ten:10", "Twelve:12", "Thirteen")
#[export(enum = (One, Two, Ten = 10, Twelve = 12, Thirteen))]
exported_enum: i64,
// @export_flags("A:1", "B:2", "AB:3")
#[export(flags = (A = 1, B = 2, AB = 3))]
flags: u32,
#[export(range = (0.0, MAX_HEALTH))]
health: f64,
#[export(flags = (A = 0b0001, B = 0b0010, C = 0b0100, D = 0b1000))]
flags: u32,
#[var(
hint = ENUM,
hint_string = "One,Two",
usage_flags = [EDITOR, READ_ONLY]
)]
my_field: i64,
}
signal
-
The functions it decorates (the signals) can accept parameters.
-
It will be fundamentally reworked.
#[derive(GodotClass)]
struct MyClass {}
#[godot_api]
impl MyClass {
#[signal]
fn some_signal();
#[signal]
fn some_signal_with_parameters(my_parameter: Gd<Node>);
}
rpc
-
You can use the
#[rpc]attribute to let your functions act as remote procedure calls (RPCs) in Godot. This is the Rust equivalent of GDScript’s@rpcannotation.#[rpc]is only supported for classes inheritingNode, and they need to declare aBase<T>field. -
The syntax follows GDScript’s
@rpc.
use godot::classes::multiplayer_api::RpcMode;
use godot::classes::multiplayer_peer::TransferMode;
use godot::prelude::*;
use godot::register::RpcConfig;
#[godot_api]
impl MyStruct {
#[rpc(unreliable_ordered, channel = 2)]
fn with_defaults(&mut self) {}
#[rpc(authority, unreliable_ordered, call_remote, channel = 2)]
fn explicit(&mut self) {}
#[rpc(config = MY_RPC_CONFIG)]
fn external_config_const(&mut self) {}
#[rpc(config = my_rpc_provider())]
fn external_config_fn(&mut self) {}
}
const MY_RPC_CONFIG: RpcConfig = RpcConfig {
rpc_mode: RpcMode::AUTHORITY,
transfer_mode: TransferMode::UNRELIABLE_ORDERED,
call_local: false,
channel: 2,
};
fn my_rpc_provider() -> RpcConfig {
RpcConfig {
transfer_mode: TransferMode::UNRELIABLE_ORDERED,
channel: 2,
..Default::default() // only possible in fn, not in const.
}
}
func
-
You can use the
#[func]attribute to declare your own functions. These are exposed to Godot and callable from GDScript.
Special
gd_extension
-
Proc-macro attribute to be used in combination with the
ExtensionLibrarytrait.
#[gdextension]
godot_dyn
#[godot_dyn]
-
Generates a
Class->dyn Traitupcasting relation. -
This attribute macro can be applied to
impl MyTrait for MyClassblocks, whereMyClassis aGodotClass. -
It will automatically implement
MyClass: AsDyn<dyn MyTrait>for you. -
Establishing this relation allows godot-rust to upcast
MyGodotClasstodyn Traitinside the library’sDynGdsmart pointer.
Attribute Macros: Class
class(base=algo)
-
Unlike C++, Rust doesn’t really have inheritance, but the GDExtension API lets us “inherit” from a Godot-provided engine class.
-
By default, classes created with this library inherit from
RefCounted, like GDScript. -
To specify a different class to inherit from, add
#[class(base = Base)]as an annotation on yourstruct:
#[derive(GodotClass)]
#[class(init, base=Node2D)]
struct MyStruct {
// ...
}
-
If you need a reference to the base class, you can add a field of type
Base<T>. The derive macro will pick this up and wire your object accordingly. You can access it throughself.base()andself.base_mut()methods.
#[derive(GodotClass)]
#[class(init, base=Node2D)]
struct MyStruct {
base: Base<Node2D>,
}
class(init)
-
If you don’t override
init()manually (within a#[godot_api]block), gdext can generate a default constructor for you. This constructor is made available to Godot and lets you callMyStruct.new()from GDScript. -
The generated
initfunction will initialize each struct field (except the field of typeBase<T>, if any) usingDefault::default().
#[derive(GodotClass)]
#[class(init)]
struct MyStruct {
// ...
}
-
To assign some other value, annotate the field with
#[init(val = ...)]:
#[derive(GodotClass)]
#[class(init)]
struct MyStruct {
#[init(val = 42)]
my_field: i64
}
class(no_init)
-
You can also disable construction from GDScript. This needs to be explicit via
#[class(no_init)]. Simply omitting theinit/no_initkeys and not overriding your own constructor will cause a compile error.
#[derive(GodotClass)]
#[class(no_init)]
struct MyStruct {
// ...
}
class(tool)
#[class(tool)]
-
Its lifecycle methods (
ready(),process()etc.) will be invoked in the editor. -
Very similar to
@tool.
class(editor_plugin)
-
It will be turned into an editor plugin. The class must then inherit from
EditorPlugin, and an instance of that class will be automatically added to the editor when launched. -
Usually combined with
tool.
class(rename)
-
You may want to have structs with the same name. With Rust, this is allowed using
mod. However, in GDScript there are no modules, namespaces, or any such disambiguation. Therefore, you need to change the names before they can get to Godot. You can use therenamekey while defining yourGodotClassfor this.
mod animal {
#[derive(GodotClass)]
#[class(init, rename=AnimalToad)]
pub struct Toad {}
}
mod npc {
#[derive(GodotClass)]
#[class(init, rename=NpcToad)]
pub struct Toad {}
}
-
These classes will appear in the Godot editor and GDScript as “AnimalToad” or “NpcToad”.
class(internal)
-
Registers a class with Godot, but does not have it show up in the editor.
#[derive(GodotClass)]
#[class(base=Node, init, internal)]
pub struct Foo {}
Objects
Gd<T>
What it stores
-
Can only hold instances of Godot classes (
Node,RefCounted, etc.) or user-declared structs (declared with#[derive(GodotClass)]).-
It does not hold built-in types (
Vector3,Color,i32).
-
-
Gd<T>never holds null objects.-
If you need nullability, use
Option<Gd<T>>. To pass null objects to engine APIs, you can additionally useGd::null_arg()as a shorthand.
-
Memory
-
The memory management strategy is fully dependent on
T:-
Objects that inherit from
RefCounted:-
Every time a smart pointer is shared using
Clone::clone(), the reference counter is incremented, and every time one is dropped, it is decremented. -
It ensures that the last reference (either in Rust or Godot) will deallocate the object and call
T’s destructor.
-
-
Manual :
-
Objects inheriting from
Objectwhich are notRefCounted(or inherited) are manually-managed ; most notably, this includes allNodeclasses. -
Their destructor is not automatically called (unless they are part of the scene tree).
-
Creating a
Gd<T>means that you are responsible for explicitly deallocating such objects usingfree().
-
-
Dynamic :
-
Due to polymorphism, a
Gd<Object>(T=Object) can point to either reference-counted or manually-managed types at runtime. -
Note that if the dynamic type is also
Object, the memory is manually-managed.
-
-
Construction
-
To construct default instances of various
Gd<T>types, there are extension methods on the typeTitself:-
Manually managed :
-
NewAlloc::new_alloc().
-
-
Reference-counted :
-
NewGd::new_gd().-
Gd::<T>::default()is equivalent to the shorterT::new_gd()and primarily useful for derives or generics.
-
-
Gd::default().-
For reference-counted types that are constructible. For user types, this means they must expose an
initfunction or have a generated one.
-
-
-
Singletons :
-
T::singleton()(inherent).
-
-
-
Gd::from_init_fn(function).-
For Rust objects with
Base<T>field, which are constructed inside the smart pointer. This is a very handy function if you want to pass extra parameters to your object upon construction.
-
-
Gd::from_object(rust_obj).-
For existing Rust objects without a
Base<T>field that are moved into the smart pointer.
-
Bind Guards (I think this is Ownership)
-
The
bind()andbind_mut()methods allow you to obtain a shared or exclusive guard to the user instance. -
These provide interior mutability similar to
RefCell, with the addition thatGdsimultaneously handles reference counting (for some typesT). -
Dropping :
-
By closing a
{ }block or callingstd::mem::drop(). -
Holding a bind guard will prevent other code paths from obtaining their own shared/mutable bind. As such, you should drop the guard as soon as you don’t need it anymore.
-
-
When you declare a
#[func]method on your own class, and it accepts&selfor&mut self, an implicitbind()orbind_mut()call on the owningGd<T>is performed.-
This is important to keep in mind, as you can get into situations that violate dynamic borrow rules; for example if you are inside a
&mut selfmethod, make a call to GDScript and indirectly call another method on the same object (re-entrancy).
-
Obtaining References
-
Gd::from_instance_id(id)andGd::try_from_instance_id(id)to obtain a pointer to an object which is already alive in the engine.
Gd Mut
-
Mutably/exclusively bound reference guard for a
Gdsmart pointer. -
See
Gd::bind_mutfor usage.
Gd Ref
-
Immutably/shared bound reference guard for a
Gdsmart pointer. -
See
Gd::bindfor usage.
Gd<T>: Base
Base
-
Restricted version of
Gd, to hold the base instance inside a user’sGodotClass. -
Behaves similarly to
Gd, but is more constrained. Cannot be constructed by the user.
Base Mut
-
Mutable/exclusive reference guard for a
Basepointer -
This can be used to call methods on the base object of a Rust object, which takes
&selfor&mut selfas the receiver. -
See
WithBaseField::base_mut()for usage.
Base Ref
-
Shared reference guard for a
Basepointer. -
This can be used to call methods on the base object of a Rust object that takes
&selfas the receiver. -
See
WithBaseField::base()for usage.
Gd<T>: DynGd
Bounds
-
Declarertells you whether the class is provided by the engine or user-defined.-
DeclEngineis used for all classes provided by the engine (e.g.Node3D). -
DeclUseris used for all classes defined by the user, typically through#[derive(GodotClass)].
-
-
Memoryis used to check the memory strategy of the static type.
This is useful when you operate on associated functions ofGd<T>orT, e.g. for construction.-
MemRefCountedis used forRefCountedclasses and derived. -
MemManualis used forObjectand all inherited classes, which are notRefCounted(e.g.Node).
-
Examples
-
Declare a custom smart pointer which wraps
Gd<T>pointers, but only acceptsTobjects that are manually managed:use godot::prelude::*; use godot::obj::{bounds, Bounds}; struct MyGd<T> where T: GodotClass + Bounds<Memory = bounds::MemManual> { inner: Gd<T>, }
OnReady
-
OnReady .
use godot::prelude::*;
#[derive(GodotClass)]
#[class(base = Node)]
struct MyClass {
base: Base<Node>,
auto: OnReady<i32>,
manual: OnReady<i32>,
}
#[godot_api]
impl INode for MyClass {
fn init(base: Base<Node>) -> Self {
Self {
base,
auto: OnReady::new(|| 11),
manual: OnReady::manual(),
}
}
fn ready(&mut self) {
// self.auto is now ready with value 11.
assert_eq!(*self.auto, 11);
// self.manual needs to be initialized manually.
self.manual.init(22);
assert_eq!(*self.manual, 22);
}
}
use godot::prelude::*;
#[derive(GodotClass)]
#[class(init, base = Node)]
struct MyClass {
base: Base<Node>,
#[init(node = "ChildPath")]
auto: OnReady<Gd<Node2D>>,
#[init(val = OnReady::manual())]
manual: OnReady<i32>,
}
#[godot_api]
impl INode for MyClass {
fn ready(&mut self) {
// self.node is now ready with the node found at path `ChildPath`.
assert_eq!(self.auto.get_name(), "ChildPath".into());
// self.manual needs to be initialized manually.
self.manual.init(22);
assert_eq!(*self.manual, 22);
}
}
Notes
use godot::{
prelude::*,
classes::Sprite2D,
};
#[derive(GodotClass)]
#[class(base=Node)]
struct Visuals {
slime: Option<Gd<Sprite2D>>,
base: Base<Node>
}
#[godot_api]
impl INode for Visuals {
fn init(base: Base<Node>) -> Self {
Self {
slime: None,
base
}
}
fn ready(&mut self,) {
self.slime = Some(self.base().get_node_as::<Sprite2D>("Slime"));
}
fn physics_process(&mut self, delta: f64) {
let new_position = self.slime().get_position() + Vector2::new(10.0 * delta as f32, 0.0);
self.slime().set_position(new_position);
let angular_speed = 1.0;
let new_rotation = self.slime().get_rotation() + (angular_speed * delta as f32);
self.slime().set_rotation(new_rotation);
}
}
impl Visuals {
fn slime(&mut self) -> &mut Gd<Sprite2D> {
self.slime.as_mut().unwrap()
// as_mut()
// Creates a new "mutable reference".
// "Converts self (an Option) into a mutable reference to the contained value, if it exists. Returns Option<&mut T>."
//-> &mut Gd<Sprite2D>
// as_deref_mut
// Same as `as_mut`, but then ownership is immediately taken.
// "After `as_mut`, the deref_mut() method returns the mutable reference to the 'target value' of T."
// "`as_mut` is enough because you only need a mutable reference to the `Gd<Sprite2D>` itself, not the internal 'target value' (`Sprite2D`)."
//-> &mut Sprite2D
}
}
Threads
-
As a rule of thumb, if you must use threading, prefer to use Rust threads over Godot threads.
-
The Cargo feature
experimental-threadsprovides experimental support for multithreading. The underlying safety rules are still being worked out, as such you may encounter unsoundness and an unstable API. -
Godot’s own thread safety rules apply. Types in this crate implement (or don’t implement)
SendandSyncwherever appropriate, but the Rust compiler cannot check what happens to an object through C++ or GDScript.